import sys
import Test_rc
from PyQt5.QtWidgets import QMainWindow, QApplication, QWidget, QLabel
from Ui_HandUI import Ui_Form
from RotatableButton import RotatableButton
from PyQt5.QtCore import QTimer, Qt
from PyQt5.QtSerialPort import QSerialPortInfo
from WBSwitchButton import WBSwitchButton
import serial
from PyQt5.QtGui import QPixmap

# 通信协议
# 此次通信全部是使用小端数据解析发送
# /*
#     HMI 上位机通信协议，此次通信全部是使用小端数据解析发送
# */
# /*
#     @ FRAME_HEAD(2b) + DATA_LEN(DATA) + DATA(nb) + SUM_CHECK(1b)
#     @ SUM_CHECK 校验和为除帧头两字节外其余所有数据累加和的低八位
# */
# // 帧头
# #define HMI_FRAME_HEAD_0                0xA3
# #define HMI_FRAME_HEAD_1                0xB4

# 获取夹爪末端指尖力
#  |帧头 |数据长度|获取末端数据指令|保留位|校验和|
#   A3 B4   02          52         00     54

# 夹爪通过RS485总线返回数据：
# | 帧头 |数据长度|末端数据指令|保留位|             数据            |校验和|
#  A3 B4	08	      53	    01	 03 00(Fx)00 00(Fy)02 00(Fz)	61

# 夹爪夹取指令
# A3 B4	04	03	00	F4	01	FC

# 夹爪复位指令
# A3 B4	01	01	02

# 获取夹爪状态指令
# A3 B4	01	08	09

# 夹爪状态返回指令
# 夹取状态值08后面这一字节（00--空闲,01--夹取中,10--判断夹稳,11--已夹稳）
# | 帧头 |数据长度|末端数据指令|状态值|物体硬度|校验和|
#  A3 B4	04	      08	    00	 F4 01	  0C

# 夹爪末端强制校准指令
#  A3	B4	01	64	65

# 夹爪切换控制模式指令
# A3 B4 01 02 03

# 通过RS485总线发送位置指令给夹爪
# 位置（小端序 EC 04-->1206）（最大2940）（最小280）
# 速度（小端序 00 00-->0）（最大1200）（最小200）
# A3 B4	08	10	01	00	00	EC	04	FF	FF	07


# 帧头
HMI_FRAME_HEAD_0 = 0xA3
HMI_FRAME_HEAD_1 = 0xB4

# 动作指令
CLAMP_FORCED_CALIBRATION = 0x64 # 强制校准
HMI_ACTION_STANDBY = 0x01       # 待命/复位
HMI_ACTION_SERVO = 0x02         # 位置控制
HMI_ACTION_SCRIPT = 0x03        # 夹取动作指令
HMI_ACTION_SERVO_TARGET = 0x10  # 位置指令
ACTION_SCRIPT_pick_up = 0       # 夹取

CLAMP_GET_ENDDATA = 0x52        # 获取末端指尖数据指令
CLAMP_GET_PINCH_STATUS = 0x08   # 获取夹取状态
CLAMP_DIMENSION = 0x01          # 维度

# 定时器间隔
TIMER_INTERVAL_SYNC_STATUS = 10

class MyMainWindow(QMainWindow):
    def __init__(self, parent=None):
        super(MyMainWindow, self).__init__(parent)

        # 初始化
        self.serial_port_name = "COM1"     # 串口号
        self.baud_rate = 1000000           # 波特率
        self.data_bits = 8                 # 数据位
        self.stop_bits = 1                 # 停止位
        self.parity = "None"               # 校验位
        self.flow_control = "None"         # 流控制
        self.serial_port = None            # 串口对象
        self.serial_status = False         # 连接状态
        self.timer = QTimer()              # 读取数据定时器
        self.timer_running = False         # 定时器状态
        self.timer.timeout.connect(self.check_serial)
        self.m_receiveBuffer = bytearray() # 读取的数据
        
        # 初始化主窗口
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        self.Logo_Label = QLabel(central_widget)
        self.Logo_Label.setFixedSize(120,120)
        self.Logo_Label.move(10,10)
        
        # 初始化UI
        self.HandUi = Ui_Form()
        self.HandUi.setupUi(central_widget)
        
        # 添加旋转按钮
        self.updataBtn = RotatableButton()
        self.updataBtn.setFixedSize(35, 35)
        self.HandUi.horizontalLayout_2.insertWidget(2, self.updataBtn)

        # 创建滑块按钮实例化
        self.SwitchBtn = WBSwitchButton(central_widget)
        self.SwitchBtn.setFixedSize(80,35)
        self.SwitchBtn.set_wait_model(False);
        self.SwitchBtn.set_switch(False)
        self.HandUi.horizontalLayout_10.insertWidget(4,self.SwitchBtn)
        self.SwitchBtn.sigSwitchChanged.connect(self.On_Conenect_Off)
        
        # 信号连接
        self._connect_signals()
        # 样式设置
        self._setup_styles()
        # 初始状态
        self.pushButton_clicked()           # 初始刷新串口
        self.Logo_Label.setStyleSheet("""
                                      QLabel{
                                        border-image: url(:/Pix/Png/HANG_LOGO1.png);/*自定义图片填充*/
                                      }
                                      """)
        self.centralWidget().setStyleSheet("""
            QWidget { background-color: #1e1f26; color: #dfe3ec; }
            QGroupBox { border: 1px solid #2f323c; border-radius: 8px; margin-top: 14px; font-size: 14px; font-weight: 600; padding: 10px; background:#262830; }
            QGroupBox::title { subcontrol-origin: margin; left: 12px; padding: 0 6px; background:#1e1f26; color:#58c4ff; }
            QLabel { font-size: 13px; }
            QComboBox { background:#2b2f38; border:1px solid #3c404a; border-radius:5px; padding:4px 8px; min-width:90px; }
            QComboBox:focus { border:1px solid #5ab0ff; }
            QComboBox::drop-down { width: 22px; border-left:1px solid #3c404a; background:#2b2f38; }
            QComboBox::down-arrow { image: url(:/Pix/Png/Down.png); }
            QPushButton { background: qlineargradient(x1:0,y1:0,x2:1,y2:1, stop:0 #3d7bd9, stop:1 #285fa8); border:none; border-radius:6px; padding:8px 18px; font-size:14px; }
            QPushButton:hover { background: qlineargradient(x1:0,y1:0,x2:1,y2:1, stop:0 #4d8ceb, stop:1 #2e6bbd); }
            QPushButton:pressed { background:#234c85; }
            QPushButton#btn_send_position { background: qlineargradient(x1:0,y1:0,x2:1,y2:1, stop:0 #27ae60, stop:1 #1d8649); }
            QPushButton#btn_send_position:hover { background: qlineargradient(x1:0,y1:0,x2:1,y2:1, stop:0 #32c16e, stop:1 #249d55); }
            QSlider::groove:horizontal { height:6px; background:#3a3f4b; border-radius:3px; }
            QSlider::sub-page:horizontal { background:#4c9dff; border-radius:3px; }
            QSlider::add-page:horizontal { background:#2a2d35; border-radius:3px; }
            QSlider::handle:horizontal { background:#ffffff; width:16px; margin:-6px 0; border-radius:8px; border:2px solid #4c9dff; }
            QSlider::handle:horizontal:hover { background:#e8f6ff; }
            QSlider::handle:horizontal:pressed { background:#cbe9ff; }
            #label_pos_value { border:1px solid #3c404a; background:#2b2f38; border-radius:4px; padding:2px 6px; }
        """)
        self.Logo_Label.raise_()
        # 新增: 如果UI含有位置滑动条则初始化其事件
        if hasattr(self.HandUi, 'slider_position'):
            self.HandUi.slider_position.valueChanged.connect(self.on_slider_changed)
        if hasattr(self.HandUi, 'btn_send_position'):
            self.HandUi.btn_send_position.clicked.connect(self.on_send_position_clicked)
        # 调试开关
        self.debug = True

    def toggle_debug(self, enable: bool | None = None):
        """打开/关闭帧调试打印; enable=None 时取反。"""
        if enable is None:
            self.debug = not getattr(self, 'debug', True)
        else:
            self.debug = bool(enable)
        print(f"[DEBUG] 帧打印调试已 {'开启' if self.debug else '关闭'}")

    def Set_Sheert(self):
        self.SwitchBtn.setStyleSheet("""
                                    QWidget{
                                        background-color: transparent; 
                                        border: 2px solid #3498db;
                                    }
                                    """)

    # 设置串口波特率函数
    def Set_Serial_Port(self, serial_port, baud_rate, data_bits, stop_bits):
        if self.serial_status:
            print("串口已连接，无需重复连接！")
            return
        
        try:
            # 配置参数映射
            data_bits_map = {
                8: serial.EIGHTBITS,
                7: serial.SEVENBITS,
                6: serial.SIXBITS,
                5: serial.FIVEBITS
            }
            
            stop_bits_map = {
                1: serial.STOPBITS_ONE,
                1.5: serial.STOPBITS_ONE_POINT_FIVE,
                2: serial.STOPBITS_TWO
            }
            
            # 创建串口连接
            self.serial_port = serial.Serial(
                port=serial_port,
                baudrate=baud_rate,
                bytesize=data_bits_map.get(data_bits, serial.EIGHTBITS),
                stopbits=stop_bits_map.get(stop_bits, serial.STOPBITS_ONE),
                parity=serial.PARITY_NONE,  # 默认无校验
                timeout=1  # 读写超时1秒
            )
            QTimer.singleShot(50, self._confirm_connection)
        except Exception as e:
            print(f"串口连接失败: {e}")
            self.serial_status = False

    def _confirm_connection(self):
        """确认串口连接状态"""
        if self.serial_port and self.serial_port.is_open:
            self.serial_status = True
            print("串口连接成功！")
            self.timer.start(TIMER_INTERVAL_SYNC_STATUS)
            self.timer_running = True
        else:
            print("串口连接失败！")

    def check_serial(self):
        if self.timer_running:
            if self.serial_port.in_waiting > 0:
                self.Recv_All()

    def disconnect_serial(self):
        # 检查定时器，并关闭
        if self.timer_running:
            self.timer.stop()
            self.timer_running = False
        if self.serial_port and self.serial_status:
            self.serial_port.close()
            self.serial_status = False
            print("串口已断开")
       
    # 开关槽函数
    def On_Conenect_Off(self,is_On_Off):
        if is_On_Off:
            # 开关打开，获取下拉框选项
            self.portName = self.HandUi.comboBox_3.currentText()
            self.portindex = self.HandUi.comboBox_2.currentIndex()
            match self.portindex:
                case 0:
                    self.port = 1000000
                case 1:
                    self.port = 921600
                case 2:
                    self.port = 512000
                case 3:
                    self.port = 230400
                case 4:
                    self.port = 115200
                case 5:
                    self.port = 9600
                case 6:
                    self.port = 4800
            self.portdata_bits = self.HandUi.comboBox_5.currentIndex()
            match self.portdata_bits:
                case 0:
                    self.portdata = 8
                case 1:
                    self.portdata = 7
                case 2:
                    self.portdata = 6
                case 3:
                    self.portdata = 5
            self.stopdata_bits = self.HandUi.comboBox_4.currentIndex()
            match self.stopdata_bits:
                case 0:
                    self.stopdata = 1
                case 1:
                    self.stopdata = 1.5
                case 2:
                    self.stopdata = 2
            # 设置给串口
            self.Set_Serial_Port(self.portName,self.port,self.portdata,self.stopdata)
        else:
            self.disconnect_serial()

    # 帮助: 追加16位小端
    @staticmethod
    def append_u16_le(container: bytearray, value: int):
        container.append(value & 0xFF)
        container.append((value >> 8) & 0xFF)

    def _build_frame(self, data_bytes: bytes) -> bytearray:
        buffer = bytearray()
        buffer.extend((HMI_FRAME_HEAD_0, HMI_FRAME_HEAD_1))
        buffer.append(len(data_bytes))
        buffer.extend(data_bytes)
        check_sum = sum(buffer[2:]) & 0xFF
        buffer.append(check_sum)
        return buffer

    def _print_frame(self, buffer: bytearray, note: str = ""):
        if not self.debug:
            return
        head = ' '.join(f'{b:02X}' for b in buffer[:2])
        length = f'{buffer[2]:02X}'
        data = ' '.join(f'{b:02X}' for b in buffer[3:-1])
        checksum = f'{buffer[-1]:02X}'
        print(f"[FRAME]{' '+note if note else ''} HEAD={head} LEN={length} DATA=[{data}] SUM={checksum} RAW={' '.join(f'{b:02X}' for b in buffer)}")

    # 发送数据 (即使未连接也打印帧内容便于调试)
    def Send_Msg(self, data) -> bool:
        try:
            # 允许多种类型输入
            if isinstance(data, bytearray):
                data_bytes = bytes(data)
            elif isinstance(data, bytes):
                data_bytes = data
            elif isinstance(data, (list, tuple)):
                data_bytes = bytes(data)
            else:
                # 兼容 Qt QByteArray 接口 (若存在)
                try:
                    data_bytes = bytes([data.at(i) for i in range(data.size())])
                except Exception:
                    raise TypeError("不支持的数据类型, 需要 bytes/bytearray/list/tuple")

            frame = self._build_frame(data_bytes)
            self._print_frame(frame)

            if not self.serial_status or not self.serial_port:
                print("[WARN] 串口未连接, 仅打印帧, 未真正发送")
                return False

            self.serial_port.write(frame)
            self.serial_port.flush()
            return True
        except Exception as e:
            print(f"发送错误: {e}")
            return False

    def _connect_signals(self):
        """连接所有信号和槽"""
        # 手部UI按钮连接
        if hasattr(self.HandUi, 'pushButton_2'):
            self.HandUi.pushButton_2.clicked.connect(self.on_pushButton_2_clicked)
        else:
            print("警告: 未找到 pushButton_2 控件")

        if hasattr(self.HandUi, 'pushButton_3'):
            self.HandUi.pushButton_3.clicked.connect(self.on_pushButton_clicked)
        else:
            print("警告: 未找到 pushButton_3 控件")

        
        # 旋转按钮
        self.updataBtn.clicked.connect(self.pushButton_clicked)
        # 滑动条与发送按钮（若存在）
        if hasattr(self.HandUi, 'slider_position'):
            self.HandUi.slider_position.valueChanged.connect(self.on_slider_changed)
        if hasattr(self.HandUi, 'btn_send_position'):
            self.HandUi.btn_send_position.clicked.connect(self.on_send_position_clicked)

    def _setup_styles(self):
        self.updataBtn.setStyleSheet("""
            QPushButton{
                border: none;  
                background-color: transparent; 
                border-image: url(:/Pix/Png/Flushed.png);
            }
        """)

    # 夹取
    def on_pushButton_2_clicked(self):
        if not self.serial_status:
            return
        data_buffer = bytearray()
        data_buffer.append(HMI_ACTION_SCRIPT)
        data_buffer.append(ACTION_SCRIPT_pick_up)
        data_buffer.append(0xF4)
        data_buffer.append(0x01)
        self.Send_Msg(data_buffer)

    # 复位
    def on_pushButton_clicked(self):
        if not self.serial_status:
            return
        data_buffer = bytearray()
        data_buffer.append(HMI_ACTION_STANDBY)
        self.Send_Msg(data_buffer)

    def resizeEvent(self, event):
        super().resizeEvent(event)

    def pushButton_clicked(self):
        self.updataBtn.startRotation(1000, 0, 360)
        QTimer.singleShot(1000, self.refresh_serial_ports)

    def refresh_serial_ports(self):
        self.HandUi.comboBox_3.clear()
        for info in QSerialPortInfo.availablePorts():
            self.HandUi.comboBox_3.addItem(info.portName())

    # 读取数据函数
    def Recv_All(self):
        if not self.serial_status:
            return
        # 读取全部数据
        data = self.serial_port.readall()
        if data:
            self.m_receiveBuffer.extend(data)
        # 判断数据长度以及帧头
        while len(self.m_receiveBuffer) >= 3:
            if (self.m_receiveBuffer[0] != HMI_FRAME_HEAD_0 or 
                self.m_receiveBuffer[1] != HMI_FRAME_HEAD_1):
                self.m_receiveBuffer.pop(0)
                continue
            dataLen = self.m_receiveBuffer[2]
            frameLen = dataLen + 4
            # 检查数据长度
            if frameLen < 6 or frameLen > 256:
                self.m_receiveBuffer.pop(0)
                continue
            if len(self.m_receiveBuffer) < frameLen:
                break
            frame = self.m_receiveBuffer[:frameLen]
            self.m_receiveBuffer = self.m_receiveBuffer[frameLen:]
            # 校验
            if self.CRC_Verify(frame):
                # 解析
                self.processFrame(frame)
            else:
                # 发送读取错误，暂时信号无用
                # self.errorOccurred.emit("Checksum mismatch")
                print("Checksum mismatch")
    # 校验函数
    def CRC_Verify(self, frame):
        checksum = 0
        for i in range(2, len(frame) - 1):
            checksum += frame[i]
        return (checksum & 0xFF) == frame[-1]
    
    # 解析函数
    def processFrame(self, frame):
        msgTypes = frame[3]
        handStates = frame[4]
        if msgTypes == CLAMP_GET_ENDDATA:
            if handStates == 0x00:
                # 末端三维力的数据
                x = frame[5] | (frame[6] << 8)
                y = frame[7] | (frame[8] << 8)
                z = frame[9] | (frame[10] << 8)

    # 新增: 滑动条变化显示
    def on_slider_changed(self, value:int):
        if hasattr(self.HandUi, 'label_pos_value'):
            self.HandUi.label_pos_value.setText(str(value))

    # 新增: 发送位置按钮
    def on_send_position_clicked(self):
        if not self.serial_status:
            print("串口未连接，无法发送位置")
            return
        if not hasattr(self.HandUi, 'slider_position'):
            return
        target = self.HandUi.slider_position.value()
        # 协议: A3 B4 + len + 10 + 01 + 00 00 + pos(lo hi) + FF FF + checksum
        # 这里只构造 data 部分: 10 01 00 00 <pos_lo> <pos_hi> FF FF
        pos_lo = target & 0xFF
        pos_hi = (target >> 8) & 0xFF

        # 先发送切换位置控制模式指令
        mode_buffer = bytearray([HMI_ACTION_SERVO, 0x02])
        self.Send_Msg(mode_buffer)

        data_buffer = bytearray([
            HMI_ACTION_SERVO_TARGET,  # 0x10 位置指令
            0x01,                     # 子命令/维度 (假设 0x01)
            0xDC, 0x05,               # 速度(占位, 可后续扩展)
            pos_lo, pos_hi,           # 位置 小端
            0xFF, 0xFF                # 末尾保留(示例使用)
        ])
        ok = self.Send_Msg(data_buffer)
        if ok:
            print(f"已发送位置: {target}")
        else:
            print("位置发送失败")

if __name__ == "__main__":
    app = QApplication(sys.argv)
    myWin = MyMainWindow()
    myWin.show()
    myWin.setWindowTitle("例程")
    sys.exit(app.exec_())
